/**
 * Copyright (c) 2010 BullionXpress.com,
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package com.bullionxpress.server.impl;

import com.bullionxpress.shared.service.AccountNotFoundException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.bullionxpress.server.application.ServerGlobals;
import com.bullionxpress.shared.model.Instrument;
import com.bullionxpress.shared.service.PositionService;
import java.rmi.RemoteException;
import com.bullionxpress.shared.model.Account;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;
import com.bullionxpress.shared.model.Position;
import com.bullionxpress.shared.service.DuplicatePositionIDException;
import com.bullionxpress.shared.service.InstrumentDatabase;
import com.bullionxpress.shared.service.PositionDatabase;
import com.bullionxpress.shared.service.PositionNotFoundException;
import com.bullionxpress.shared.util.Log;

/**
 *
 * @author BullionXpress.com
 */
public class PositionManager extends java.rmi.server.UnicastRemoteObject implements PositionService, PositionDatabase {

    Connection conn = null;
    Statement statement = null;

    public PositionManager() throws RemoteException
    {
            connect();
    }

   public void connect() {
        try {
            conn = DatabaseManager.getConnection();

            try {
                statement = conn.createStatement();
                statement.executeUpdate("CREATE TABLE IF NOT EXISTS positions (accountid varchar(256), symbol varchar(256), id varchar(128), entryPrice double, units long)");
            } catch (Exception ex) {
                System.out.println(ex);
                ex.printStackTrace();
            }
        } catch (Exception ex) {
            System.out.println(ex);
            ex.printStackTrace();
        }
    }

    public Position[] getAll() {
        try {
            String query = "SELECT * FROM positions";
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            ResultSet result = statement.executeQuery(query);
            Vector v = new Vector();
            while (result.next()) {
                Position pos = get(result.getString("id"));
                v.addElement(pos);
            }
            Position[] positionList = new Position[v.size()];
            for (int i = 0; i < v.size(); i++) {
                positionList[i] = (Position) v.get(i);
            }
            return positionList;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public Position get(String id) throws PositionNotFoundException {

        if (id == null) {
            throw new IllegalArgumentException("Position id cannot be null");
        }

        String query = "SELECT * FROM positions WHERE id = '" + id + "'";
        ResultSet result = null;
        boolean r = false;
        try {
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            result = statement.executeQuery(query);
            r = result.next();
        } catch (Exception ex) {
        }

        if (r) {
            Position pos = new Position();
            try {
                InstrumentDatabase db = new InstrumentManager();
                db.connect();
                String symbol = result.getString("symbol");
                Instrument instrument = db.get(symbol);
                String aid = result.getString("accountid");

                AccountRegistrar ar = new AccountRegistrar();
                Account account = ar.get(aid);
                pos.setAccount(account);
                pos.setId(result.getString("id"));
                pos.setInstrument(instrument);
                pos.setUnits(result.getLong("units"));
                pos.setEntryPrice(result.getDouble("entryPrice"));
                //pos.setOwner(result.getString("owner"));
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new PositionNotFoundException();
            }
            return pos;
        } else {
            //Log.info("Position not found");
            throw new PositionNotFoundException();
        }
    }

    public void create(Position b) throws DuplicatePositionIDException {

        if (b == null) {
            throw new IllegalArgumentException();
        } else if (b.getId() == null) {
            throw new IllegalStateException("Cannot have an unset ID");
        } 
        try {
            Position a = get(b.getId());
            throw new DuplicatePositionIDException();
        } catch (PositionNotFoundException ex) {
        }

        String query = "INSERT INTO positions (accountid, id, symbol, entryPrice, units)  VALUES ( '" + b.getAccount().getId() + "', '" + b.getId() + "', '" + b.getInstrument().getSymbol() + "', " + b.getEntryPrice() + ", " + b.getUnits() + ")";
        int n = 0;
        try {
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            n = statement.executeUpdate(query);
        } catch (SQLException ex) {
            ex.printStackTrace();
            //System.out.println(ex);
        }
        if (n != 1) {
            throw new DuplicatePositionIDException();
        }
    }

    public void delete(String id) throws PositionNotFoundException {

        if (id == null) {
            throw new IllegalArgumentException("Position id cannot be null");
        }
        String sql = "DELETE FROM positions WHERE id = '" + id + "'";
        int n = 0;
        try {
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            n = statement.executeUpdate(sql);
            Log.info("DELETE:" + n);
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        if (n < 1) {
            throw new PositionNotFoundException();
        }
    }

    public void close() {
        //try {
            //conn.close();
        //} catch (SQLException ex) {
        //    ex.printStackTrace();
        //}
    }

    public int count() {
        int r = 0;
        Position[] b = getAll();
        return b.length;
    }

    public void save(Position a) throws PositionNotFoundException {
        if (a == null) {
            throw new IllegalArgumentException("Cannot save null position");
        }
        if (a.getId() == null) {
            throw new IllegalStateException("Position cannot have an id that isn't set");
        }

        Position ac = get(a.getId());
        if (ac == null) {
            throw new PositionNotFoundException("Position " + a.getId() + " is not found");
        }

        String query = "UPDATE positions SET accountid = '" + a.getAccount().getId() + "', symbol = '" + a.getInstrument().getSymbol() + "', units = " + a.getUnits() + ", entryPrice = " + a.getEntryPrice() + " WHERE id = " + a.getId() + ";";
        int n = 0;
        try {
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            n = statement.executeUpdate(query);
        } catch (SQLException ex) {
            ex.printStackTrace();
            System.out.println(ex);
        }
        if (n != 1) {
            throw new IllegalStateException("It should be 1");
        }


    }

    public Position[] getPositionListByAccount(String aid) throws PositionNotFoundException, RemoteException {
        try {
            String query = "SELECT * FROM positions WHERE accountid = '" + aid + "'";
            conn = DatabaseManager.getConnection();
            statement = conn.createStatement();
            ResultSet result = statement.executeQuery(query);
            Vector v = new Vector();
            while (result.next()) {
                Position pos = get(result.getString("id"));
                v.addElement(pos);
            }
            Position[] positionList = new Position[v.size()];
            for (int i = 0; i < v.size(); i++) {
                positionList[i] = (Position) v.get(i);
            }
            return positionList;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    
    public void open(Position p) {
        try {
            if (p == null) {
                throw new IllegalArgumentException("Position cannot be null");
            }
            if (p.getAccount() == null) {
                throw new IllegalArgumentException("Account cannot be null for a positon");
            }
            if (p.getInstrument() == null) {
                throw new IllegalArgumentException("Position instrument cannot be null (open)");
            }
            ServerGlobals.positions.addElement(p);
            create(p);
        } catch (DuplicatePositionIDException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void close(Position p) {

        if (p == null) {
            throw new IllegalArgumentException("Position cannot be null");
        }
        if (p.getUnits() < 0) {
            throw new IllegalArgumentException("Cannot close negative units");
        }
        if (p.getAccount() == null) {
            throw new IllegalArgumentException("Account cannot be null for a positon");
        }
        if (p.getInstrument() == null) {
            throw new IllegalArgumentException("Position instrument cannot be null for a positon (close)");
        }


        Position c = getPositionByInstrument(p.getAccount().getId(), p.getInstrument());

        long _p = c.getUnits() - p.getUnits();
        if (_p < 0) {
            throw new IllegalArgumentException("Cannot create a negative exposure:" + _p);
        }

        Position np = new Position();
        np.setInstrument(p.getInstrument());
        np.setUnits(-p.getUnits());
        np.setEntryPrice(p.getEntryPrice());
        np.setAccount(p.getAccount());
        open(np);
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public Position getPositionById(Account account, String id) {
        try {
            Position[] list = getPositionListByAccount(account.getId());
            if (list != null) {
                for (int i = 0; i < list.length; i++) {
                    if (list[i] == null) {
                        throw new IllegalStateException("why is there a null position in this list?");
                    }
                    if (list[i].getId().equals(id)) {
                        return list[i];
                    }
                }
            }
        }
        catch (PositionNotFoundException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RemoteException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        }
            //throw new UnsupportedOperationException("Not supported yet.");
        
        return null;
    }

    public Position getPositionByInstrument(String a, Instrument instrument) {
        try {
            if (a == null) {
                throw new IllegalArgumentException("Cannot have null account");
            }
            if (instrument == null) {
                throw new IllegalArgumentException("Cannot have null instrument");
            }
            Position[] arr = getPositionListByAccount(a);
            if (arr == null) {
                throw new IllegalArgumentException("There are no positions for the account (" + a + ") specified!");
            }
            Vector v = new Vector();
            for (int i = 0; i < arr.length; i++) {
                Position p = arr[i];
                if (p == null) {
                    throw new IllegalStateException("There is no circumstance that should permit the existance of a null position");
                }
                Instrument instr = p.getInstrument();
                if (instr.getSymbol().equals(instrument.getSymbol())) {
                    v.addElement(arr[i]);
                }
            }
            Position p = new Position(instrument);
            Iterator i = v.iterator();
            double rtp = 0;
            long rtu = 0;
            int count = 0;
            AccountRegistrar reg = new AccountRegistrar();
            p.setAccount(reg.get(a));
            while (i.hasNext()) {
                Position _p = (Position) i.next();
                rtu += _p.getUnits();
                rtp += _p.getEntryPrice();
                count++;
            }
            rtp = rtp / count;
            p.setEntryPrice(rtp);
            p.setUnits(rtu);
            return p;
        } catch (AccountNotFoundException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (PositionNotFoundException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RemoteException ex) {
            Logger.getLogger(PositionManager.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    
}
